<?php
/**
 * Project:     drylamp - rapid application development framework for php
 * File:        CronController.php
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * Abstract class for a generic logger
 * 
 * @copyright 2009-2010 Ilya St.
 * @author Ilya St. <provided at gmail dot com>
 * @version 1.0
 */

abstract class CronController {
	
	const JOB_SUFFIX = "_job.php";
	
	const INSTALL_PARAM = "-install";
	const REMOVE_PARAM  = "-remove";
	const HELP_PARAM    = "-help";
	
	const WHITE_SPACE   = " "; 
	
	/**
	 * Default schedule 
	 */
	protected $minute  = "*";
	protected $hour    = "*";
	protected $day		 = "*";
	protected $month	 = "*";
	protected $weekDay = "*";
	
	
	/**
	 * Executes the action item.
	 */
	public static function execute() {
		$className = self::getJobClassName($_SERVER['argv'][0]);
		$obj = new $className();
	}
	
	public static function getJobClassName($pScriptName) {
		return str_replace(self::JOB_SUFFIX, "", basename($pScriptName));
	}
	
	protected static function getParam($p) {
		return strtolower($p);
	}
	
	protected static function reportUnknownParamAndDie($pParam) {
		die("Unrecognized parameter ". $pParam. ".\n". self::getHelpText());
	}
	
	protected static function getHelpText() {
		$maxLength = max(max(self::INSTALL_PARAM, self::REMOVE_PARAM), self::HELP_PARAM);

		$helpText = "\nUsage: [job_name]_job.php [PARAM]\n".
								"where PARAM can have one out of the following values:\n".
								str_pad(self::INSTALL_PARAM,$maxLength)  . " - installs the job into the crontab\n".
								str_pad(self::REMOVE_PARAM ,$maxLength)  . " - removes the job from the crontab\n".
								str_pad(self::HELP_PARAM   ,$maxLength) . " - show help\n".
								"\nIf no parameter specified, job will be executed once, but will not be scheduled.\n";
		return $helpText;	 
	}
	
	public static function showHelpAndDie() { 
		die(self::getHelpText());
	}
	
	public function __construct() {
		if ($_SERVER['argc'] == 2) {
			switch (self::getParam($_SERVER['argv'][1])) {
				case self::INSTALL_PARAM:
					$this->installJob(); 
					break;
					
				case self::REMOVE_PARAM:
					$this->removeJob(); 
					break;	
					
				case self::HELP_PARAM:
					self::showHelpAndDie();
					break;	
					
				default:
					self::reportUnknownParamAndDie($argv[1]);
					break;	
			}
		}
		 else {
		 	 $this->run();
		 }
	}
	
	public static function getCronTabListCMD() {
		$user = self::getUser();
    $listCronTabCMD = "crontab -u $user -l";
    return $listCronTabCMD;
	}
	
	/**
	 * Installs the job into the crontab
	 * Enter description here ...
	 */
	public function installJob() {
		$this->removeJob(); // to make sure there are no duplicate jobs
    $listCronTabCMD = self::getCronTabListCMD();
    $listCronTabOUT = shell_exec($listCronTabCMD);
    $newJob = $this->assembleJob();
    $newCronTab = $newJob . $listCronTabOUT;
    
    $tempCronFile = "/tmp/drycron_".time().".txt";
    file_put_contents($tempCronFile, $newCronTab);
    
    $installCronTabCMD = "crontab -u ".self::getUser()." " . $tempCronFile;
    shell_exec($installCronTabCMD);
    unlink($tempCronFile);
	}
	
	/**
	 * Creates a crontab record
	 */
	public function assembleJob() {
		$jobStr = "";
		$jobStr .= $this->minute  . self::WHITE_SPACE;
		$jobStr .= $this->hour    . self::WHITE_SPACE;
		$jobStr .= $this->day     . self::WHITE_SPACE;
		$jobStr .= $this->month   . self::WHITE_SPACE;
		$jobStr .= $this->weekDay . self::WHITE_SPACE;
		
		$jobStr .= $this->getPHPBinPath() . self::WHITE_SPACE 
		         . $this->getFullPath() . self::WHITE_SPACE . "#". $this->getJobLabel(). "\n";
		
		return $jobStr;
	}
	
	/**
	 * Returns full path to this script
	 */
	protected function getFullPath() {
		$pwd = getcwd();
		$path = "";
		if ( substr($pwd, -1) == "/" ) $path = $pwd . $_SERVER['argv'][0];
		else  $path = $pwd . "/" . $_SERVER['argv'][0];
		return $path;
	}
	
	/**
	 * Returns path to php binary
	 */
	protected function getPHPBinPath() {
		if (array_key_exists("_", $_SERVER) && is_executable($_SERVER["_"])) {
			return $_SERVER["_"];
		}
		 elseif (array_key_exists("SUDO_COMMAND", $_SERVER)) {
		 	 $cmd = $_SERVER['SUDO_COMMAND'];
		 	 $parts = explode(" ", $cmd);
		 	 if (is_executable($parts[0])) return $parts[0];
		 }
		return "php";
	}
	
	/**
	 * Returns the job label. Shoudl be overloaded in a job class if custom job label is needed.
	 */
	protected function getJobLabel() {
		$class = get_class($this);
		$label = "drycron::" . $class;
		return $label;
	}
	
	/**
	 * returns the unix user, configured to run this application
	 */
	public static function getUser() {
		$user = Conf::get('UNIX_USER');
		if (is_null($user)) {
			die("Error: UNIX_USER can't be null.\n Please edit configuration and specify a valid unix user.\n");
		}
		return $user;
	}
	
	/**
	 * Removes the job from the tab
	 */
	public function removeJob() {
		 $listCronTabCMD = self::getCronTabListCMD();
		 $listCronTabOUT = shell_exec($listCronTabCMD);
		 $label = $this->getJobLabel();
		 
		 $lines = explode("\n", $listCronTabOUT);
		 $newTab = "";
		 
		 if (is_array($lines)) foreach($lines as $line) {
		 	 if (strstr( $line , $label)) continue;
		 	 $newTab .= $line. "\n";
		 }
		 
		$tempCronFile = "/tmp/drycron_".time().".txt";
    file_put_contents($tempCronFile, $newTab);
    $installCronTabCMD = "crontab -u ".self::getUser()." " . $tempCronFile;
    shell_exec($installCronTabCMD);
		unlink($tempCronFile);
	}
	
	abstract public function run();
	
}


/**
 * Example of an extending class: 
 * 
 * //Let containing script filename be sample_name_job.php
 * 
 * // classname must be filename minus "_job.php"
 * class sample_name extends CronController {
 * 
 *    //schedule - every minute in this case
 *    protected $minute  = "*";
 *    protected $hour    = "*";
 *    protected $day     = "*";
 *    protected $month   = "*";
 *    protected $weekDay = "*"; 
 *    
 *    //the actual stuff to be done:
 *    public function run() {
 *      echo "I like turtles!\n";
 *    }
 * }
 * 
 * CronController::execute();  // - fire off the job controller
 * 
 */